// Compile:
// x86-64
// gcc -O3 -DRULES -m64 -g -Wall -ftree-parallelize-loops=4 -fomit-frame-pointer -ftree-vectorize -fno-math-errno -msse2 -march=native -mtune=native prng.c -o prng
// x86
// gcc -DRULES -m64 -O3 -g -Wall -ftree-parallelize-loops=4 prng.c -o prng

// Ststistics //
// 1GB compression ratio (best, obsolete)
// ./prng 17 | head -c 1048576015 | lzma -c9 > test.lz 1GB -> 83MB

// Single Range: ./prng | gzip -c9 > test.gz 
// Number of candidates: 	235417
// Time to create: 		~5m5.781s
// Time to create and compress: ~5m2.916s

// Single Range: ./prng | gzip -c9 > test.gz 
// Number of candidates: 	3,521,309
// Time to create and compress: ~83m47.233s


#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>

uint64_t lfsr      = 0x0; 
uint64_t mylfsr    = 0x0;
uint64_t stop_lfsr = 0x0;
uint8_t  byte;
char 	 *p; 
int 	 Decimal_Rule = 0;
int 	 Capital_Rule = 0; 
int      Range_Rule  = 0;
int 	 i;
FILE     * pFile;
char     pass[9];
char     next_state [256];

void     ex_program(int sig);
inline   int  inRange(uint8_t byte);
inline   void ifRules(uint8_t byte);
void     save_state();
inline   void lfsr2string(uint64_t Lfsr, char *dst);



int  main(int argc, char** argv)
{
  
 pFile = fopen ("state","r");
 
 if (pFile == NULL) 
 { perror ("Error opening file"); return 1; }
 else 
 {
     if ( fgets (next_state , 256 , pFile) != NULL )
     {
      #ifdef DEBUG
 		 puts (next_state);
      #endif
      fclose (pFile);
     }
     else return 2;
 }

 (void) signal(SIGINT, ex_program);

 for (i = 0; i < 8; i++)
 { 
       mylfsr += next_state[i];
       if (i<7) mylfsr <<= 8;
 }

 for (i = 8; i < 17; i++)
 { 
       stop_lfsr += next_state[i];
       if (i<16) stop_lfsr <<= 8;
 }

 lfsr = mylfsr;  

#ifdef DEBUG 
 printf("start at: %" PRIx64 "\n", lfsr);
 printf("stop  at: %" PRIx64 "\n", stop_lfsr);
#endif

 /* State Counter */
 i = 0; 

 if (lfsr == stop_lfsr)
 { 

#ifdef DEBUG
printf("Reached the end of State\n"); 
#endif

exit(1); }

do {
  lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0x800000000000000Du);    

/* TODO: SIMD implementation */   
#ifdef RULES
   Decimal_Rule = 0;
   Capital_Rule = 0; 
   Range_Rule = 0;

   byte = (lfsr & 0xFF00000000000000ULL) >> 56;
  
   if ((byte >= 97) && (byte <= 122))
   {
    pass[0] = byte;
    
    byte = (lfsr & 0x00FF000000000000ULL) >> 48;
    pass[1] = byte;
     if (  inRange(byte)  )
     {
      ifRules(byte);

      byte = (lfsr & 0x0000FF0000000000ULL) >> 40;
      pass[2] = byte;
      if (  inRange(byte)  )
      {
	ifRules(byte);
	 
        byte = (lfsr & 0x000000FF00000000ULL) >> 32;
	pass[3] = byte;
        if (  inRange(byte)  )
	{
	  ifRules(byte);

	  byte = (lfsr & 0x00000000FF000000ULL) >> 24;	
	  pass[4] = byte;
	  if (  inRange(byte)  )
	  {
	    ifRules(byte);

	    byte = (lfsr & 0x0000000000FF0000ULL) >> 16;
	    pass[5] = byte;
      	    if (  inRange(byte)  )
	    {
	       ifRules(byte);

	       byte = (lfsr & 0x000000000000FF00ULL) >> 8;
	       pass[6] = byte;
	       if (  inRange(byte)  )
	       {
	         ifRules(byte);

		 byte = (lfsr & 0x00000000000000FFULL);
		 pass[7] = byte;
		 if (  inRange(byte)  )
		 {
		  ifRules(byte);
		  Range_Rule = 1;
		  pass[8] = '\0';
	         }
		}
	    }
	   }
	  }
	}
	
     }
   }	


   if ( Decimal_Rule && Capital_Rule && Range_Rule)
   {   printf("%s\n",pass);  i++; }

   if (i == 10000)
   {
      save_state(); i = 0;
   }

#ifdef DEBUG
    printf("%16" PRIx64 ":%s\n", lfsr,pass);  
#endif

   
#endif

} while(lfsr != stop_lfsr);

  save_state();

return 0;
}


inline int  inRange(uint8_t byte)
{
  if ((((byte >= 97) && (byte <= 122))) || (((byte >= 65) && (byte <= 90))) || (((byte >= 48) && (byte <= 57))))
	return 1;

  return 0;
}

inline void  ifRules(uint8_t byte)
{
 if ( (((byte >= 65) && (byte <= 90))) ) Capital_Rule = 1;
 if ( (((byte >= 48) && (byte <= 57))) ) Decimal_Rule = 1;
}

void ex_program(int sig) {
 printf("final: %" PRIx64 "\n", lfsr);
 (void) signal(SIGINT, SIG_DFL);
 exit(0);
}

inline void lfsr2string(uint64_t Lfsr, char dts[9])
{
  dts[0] = (Lfsr & 0xFF00000000000000ULL) >> 56;
  dts[1] = (Lfsr & 0x00FF000000000000ULL) >> 48;
  dts[2] = (Lfsr & 0x0000FF0000000000ULL) >> 40;
  dts[3] = (Lfsr & 0x000000FF00000000ULL) >> 32;
  dts[4] = (Lfsr & 0x00000000FF000000ULL) >> 24;	
  dts[5] = (Lfsr & 0x0000000000FF0000ULL) >> 16;
  dts[6] = (Lfsr & 0x000000000000FF00ULL) >> 8;
  dts[7] = (Lfsr & 0x00000000000000FFULL);
  dts[8] = '\0';
}


void save_state() 
{
       char stop_state[9];      
       lfsr2string(stop_lfsr, stop_state);

       pFile = fopen ("state","w");
       if ( Decimal_Rule && Capital_Rule && Range_Rule)
       { 

#ifdef DEBUG
printf("next state: %s stop at: %s\n", pass, stop_state);
#endif
         fprintf (pFile, "%s %s\n", pass, stop_state);
       }
       else
       { 

#ifdef DEBUG
printf("next state: %s stop at: %s\n", stop_state, stop_state);
#endif
         fprintf (pFile, "%s %s\n", stop_state, stop_state);
       }
       
       fclose (pFile);
}
